import json
import os
from PyQt5.QtWidgets import (
    QHBoxLayout,
    QWidget,
    QVBoxLayout,
    QLabel,
    QStyledItemDelegate,
    QScrollArea,
    QPushButton,
    QDesktopWidget,
    QComboBox,
    QFrame,
    QGroupBox,
    QAbstractItemView,
    QTableWidgetItem,
    QTableWidget,
    QSpacerItem,
    QSizePolicy,
)
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QEvent
from PyQt5.QtGui import QPainter, QPen, QColor, QDoubleValidator
from pydantic import BaseModel, Field, field_validator
from functools import partial
import gui.controls.envs as envs
from ..basic_grid_component import BasicGridComponent
from ..dlg_model_params import DlgModelParams
from ..c_params_table import CParamsTable
from ..dlg_params import DlgParams
from ..utils import Utils
from ..custom_widget import (
    LineEditFloat,
    LineEditStr,
    LineEditInt,
    SelectBox,
    MultRadioBtnGroup,
)


from enum import Enum


class FloatValidatorDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super(FloatValidatorDelegate, self).__init__(parent)
        self.validator = QDoubleValidator(self)

    def createEditor(self, parent, option, index):
        editor = super(FloatValidatorDelegate, self).createEditor(parent, option, index)
        editor.setValidator(self.validator)
        return editor


class CustomTable(CParamsTable):

    def __init__(self, rows=0, columns=2, parent=None):
        super(CustomTable, self).__init__(rows=rows, columns=columns, parent=parent)
        self.setEditTriggers(QAbstractItemView.DoubleClicked)
        self.verticalHeader().setVisible(True)


class StrEnumWithAlias(str, Enum):
    def __new__(cls, value, alias):
        obj = str.__new__(cls, value)
        obj._value_ = value
        obj.alias = alias
        return obj


class MethodTypeEnum(StrEnumWithAlias):
    t1 = "几何参数优化", "几何参数优化"
    t2 = "复合材料铺层优化", "复合材料铺层优化"


class AlgorithmTypeEnum(StrEnumWithAlias):
    t1 = "梯度类算法SQP", "梯度类算法SQP"
    t2 = "启发式算法GA", "启发式算法GA"
    t3 = "CMA-ES", "CMA-ES"


class GoalTypeEnum(StrEnumWithAlias):
    t1 = "最大主应变最小化", "最大主应变最小化"


class ConstraintsTypeEnum(StrEnumWithAlias):
    t1 = "质量", "质量"
    t2 = "位移", "位移"


# 定义 Pydantic 模型
class OptimizationData(BaseModel):
    method: MethodTypeEnum = Field(
        description="优化方法",
        alias="method",
        required=True,
    )
    algorithm: AlgorithmTypeEnum = Field(
        description="优化算法",
        alias="algorithm",
        required=True,
    )
    goal: GoalTypeEnum = Field(
        description="优化目标",
        alias="goal",
        required=True,
    )
    constraints: ConstraintsTypeEnum = Field(
        description="约束条件",
        alias="constraints",
        required=True,
        extra={"type": "multRadio"},
    )
    wallThicknessT: float = Field(
        description="蜂窝芯子壁厚t",
        alias="wallThicknessT",
        required=True,
    )
    sideLengthL: float = Field(
        description="蜂窝芯子边长l",
        alias="sideLengthL",
        required=True,
    )
    #
    lapJointNumber: int = Field(
        description="搭接数量",
        alias="lapJointNumber",
        required=True,
        extra={"hide": True},
    )
    strengthenNumber: int = Field(
        description="加强层层数",
        alias="strengthenNumber",
        required=True,
        extra={"hide": True},
    )


class OptimizationComponent(BasicGridComponent):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.gridLayout.setContentsMargins(0, 0, 0, 0)

    def get_model(self):
        return OptimizationData

    def setDefault(self):
        self.gridWidgets["method"].setValue("几何参数优化")
        self.gridWidgets["algorithm"].setValue("梯度类算法SQP")

    def updateUi(self, data):
        for k, v in data.items():
            if isinstance(self.gridWidgets[k], QTableWidget):
                self.loadTableData(self.gridWidgets[k], v, k)
            elif isinstance(self.gridWidgets[k], QComboBox):
                self.gridWidgets[k].setCurrentText(v)
            else:
                self.gridWidgets[k].setValue(v)

    def loadTableData(self, table, data, flag):
        for index, item in enumerate(data):
            if flag == "lapJointTable":
                table.addRow([item["宽度"], item["下限"], item["初始值"], item["上限"]])
            elif flag == "strengthenTable":
                table.addRow([item["厚度"], item["下限"], item["初始值"], item["上限"]])
        table.set_columns_editable()

    def updateUI(self):
        fileds = self.model.model_fields
        flagRow = 0
        for row, (field_name, field_info) in enumerate(fileds.items()):

            buttonList = []
            disable = False
            hide = False
            type = None
            if field_info.json_schema_extra.get("extra"):
                if field_info.json_schema_extra.get("extra").get("hide"):
                    hide = field_info.json_schema_extra.get("extra").get("hide")
                if field_info.json_schema_extra.get("extra").get("hide"):
                    buttonList = field_info.json_schema_extra.get("extra").get("button")
                if field_info.json_schema_extra.get("extra").get("hide"):
                    disable = field_info.json_schema_extra.get("extra").get("disable")
                if field_info.json_schema_extra.get("extra").get("type"):
                    type = field_info.json_schema_extra.get("extra").get("type")
            if hide:
                flagRow = flagRow + 1
                continue
            row = row - flagRow
            label = QLabel(field_info.description)
            self.gridLayout.addWidget(label, 2 * row, 0)
            if issubclass(field_info.annotation, Enum):
                if type == "multRadio":
                    self.gridWidgets[field_name] = MultRadioBtnGroup(
                        options=[
                            {"value": item.alias}
                            for item in list(field_info.annotation)
                        ]
                    )
                else:
                    self.gridWidgets[field_name] = SelectBox(
                        options=[
                            {"title": item.alias, "value": item.alias}
                            for item in list(field_info.annotation)
                        ]
                    )
                    self.gridWidgets[field_name].setMinimumWidth(100)
                    # self.gridWidgets[field_name].setPlaceholderText(f"请选择{field_info.description}")
                    # self.gridWidgets[field_name].addItems(
                    #     [item.alias for item in list(field_info.annotation)]
                    # )
            else:
                field_type = field_info.annotation
                if field_type is float:
                    widget = LineEditFloat()
                elif field_type is int:
                    widget = LineEditInt()
                else:
                    widget = LineEditStr()
                if not disable:
                    widget.setPlaceholderText(f"请输入{field_info.description}")
                self.gridWidgets[field_name] = widget
            if disable:
                self.gridWidgets[field_name].setEnabled(False)
            self.gridLayout.addWidget(self.gridWidgets[field_name], 2 * row, 1)
            if row != 0:
                spacer = QSpacerItem(
                    20, 100, QSizePolicy.Minimum, QSizePolicy.Expanding
                )
                self.gridLayout.addItem(
                    spacer, 2 * row - 1, 0, 1
                )  # 在第1行添加一个占位符，占据第0列和第1列
            for i, btnItem in enumerate(buttonList):
                self.gridWidgets[btnItem.get("name")] = QPushButton(
                    btnItem.get("title")
                )
                self.gridWidgets[btnItem.get("name")].setMinimumWidth(
                    btnItem.get("width")
                )
                self.gridWidgets[btnItem.get("name")].setFixedHeight(30)
                self.gridLayout.addWidget(
                    self.gridWidgets[btnItem.get("name")], 2 * row, 2 + i
                )
        self.setLayout(self.gridLayout)


class DlgOptimization(DlgModelParams):
    optimization_save_signal = pyqtSignal(object)

    def __init__(self, parent=None):
        super(DlgOptimization, self).__init__(parent)

        self.setWindowTitle("优化方法")
        self.pBtnOK.setFocusPolicy(Qt.NoFocus)
        self.pBtnCancel.setFocusPolicy(Qt.NoFocus)
        self.setMinimumWidth(800)
        # self.resize(800, 400)
        layout = QVBoxLayout()
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        componentLayout = QHBoxLayout()

        self.component = OptimizationComponent()
        self.component.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored)

        componentLayout.addWidget(self.component)

        topPlyLayers = self.createLayersTable("搭接数量", "lapJoint")
        topRepairPlyLayers = self.createLayersTable("加强层层数", "strengthen")

        vLayout = QVBoxLayout()
        vLayout.addLayout(topPlyLayers)
        vLayout.addLayout(topRepairPlyLayers)

        componentLayout.addItem(
            QSpacerItem(10, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        )
        componentLayout.addLayout(vLayout)

        layout.addLayout(componentLayout)

        a = QWidget()
        a.setLayout(layout)
        self.size = a.sizeHint()
        scroll.resize(self.size)
        # a.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        # scroll.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        scroll.setWidget(a)

        self.vbox.insertWidget(0, scroll)
        # self.vbox.insertLayout(0, layout)

        self.component.setDefault()

        jsonPath = "data/ipimas/uis/Optimization.json"
        if not os.path.exists(jsonPath):
            data = Utils.read_json("data/ipimas/uis/Optimization.json")
        else:
            data = Utils.read_json(jsonPath)
        self.updateUi(data)

        self.component.gridWidgets["lapJointTable"].itemChanged.connect(
            partial(self.on_item_changed, flag="lapJoint")
        )
        self.component.gridWidgets["strengthenTable"].itemChanged.connect(
            partial(self.on_item_changed, flag="strengthen")
        )

        left, top, right, bottom = self.vbox.getContentsMargins()
        self.resize(
            a.width(),
            a.height()
            + self.pBtnCancel.sizeHint().height()
            + top
            + bottom
            + 15
            + 2
            + 10,
        )

        available_height = QDesktopWidget().availableGeometry().height() - 50
        if self.height() > available_height:
            self.resize(self.width(), available_height)

    def createLayersTable(self, title, name):
        numLayout = QHBoxLayout()
        label = QLabel(title)
        label.setFixedWidth(160)
        edit = LineEditInt()
        edit.setPlaceholderText(f"请输入{title}")
        edit.editingFinished.connect(lambda: self.on_text_changed(flag=name))
        self.component.gridWidgets[f"{name}Number"] = edit
        numLayout.addWidget(label)
        numLayout.addWidget(edit)
        numLayout.addStretch(1)
        layersLayout = QVBoxLayout()
        if name == "lapJoint":
            table = CustomTable(rows=1, columns=4)
            table.setItemDelegateForColumn(0, FloatValidatorDelegate(table))
            table.setItemDelegateForColumn(1, FloatValidatorDelegate(table))
            table.setItemDelegateForColumn(2, FloatValidatorDelegate(table))
            table.setItemDelegateForColumn(3, FloatValidatorDelegate(table))
            table.setHorizontalHeaderLabels(["宽度", "下限", "初始值", "上限"])
        elif name == "strengthen":
            table = CustomTable(rows=1, columns=4)
            table.setItemDelegateForColumn(0, FloatValidatorDelegate(table))
            table.setItemDelegateForColumn(1, FloatValidatorDelegate(table))
            table.setItemDelegateForColumn(2, FloatValidatorDelegate(table))
            table.setItemDelegateForColumn(3, FloatValidatorDelegate(table))
            table.setHorizontalHeaderLabels(["厚度", "下限", "初始值", "上限"])

        self.component.gridWidgets[f"{name}Table"] = table
        layersLayout.addLayout(numLayout)
        layersLayout.addWidget(table)

        # 计算前三行的高度
        total_height = 0
        total_height = table.rowHeight(0) * 3
        horizontal_header_height = table.horizontalHeader().height()
        # table.setFixedHeight(total_height + horizontal_header_height)
        # table.setMinimumHeight(total_height + horizontal_header_height+20)
        table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored)
        table.setMinimumHeight(total_height + horizontal_header_height)
        table.resize(-1, total_height + horizontal_header_height)
        table.removeRow(0)  # 删除第一行

        return layersLayout

    def on_item_changed(self, item, flag):
        row = item.row()
        column = item.column()
        if column == 0:
            return
        value = toFloat(item.text())
        if value is not None:
            if flag == "lapJoint":
                title = "搭接" + str(row + 1)
            else:
                title = "加强层" + str(row + 1)
            table = self.component.gridWidgets[f"{flag}Table"]
            if column == 1:
                initValue = toFloat(table.item(row, 1).text())
                upperValue = toFloat(table.item(row, 3).text())
                if initValue is not None and value > initValue:
                    item.setText("")
                    return envs.MBox.warning(f"{title}行，下限不可大于初始值")
                if upperValue is not None and value > upperValue:
                    item.setText("")
                    return envs.MBox.warning(f"{title}行，下限不可大于上限")
            elif column == 2:
                lowerValue = toFloat(table.item(row, 1).text())
                upperValue = toFloat(table.item(row, 3).text())
                if lowerValue is not None and lowerValue > value:
                    item.setText("")
                    return envs.MBox.warning(f"{title}行，初始值不可小于下限")
                if upperValue is not None and value > upperValue:
                    item.setText("")
                    return envs.MBox.warning(f"{title}行，初始值不可大于上限")
            elif column == 3:
                lowerValue = toFloat(table.item(row, 1).text())
                initValue = toFloat(table.item(row, 2).text())
                if lowerValue is not None and value < lowerValue:
                    item.setText("")
                    return envs.MBox.warning(f"{title}行，上限不可小于下限")
                if initValue is not None and value < initValue:
                    item.setText("")
                    return envs.MBox.warning(f"{title}行，上限不可小于初始值")

    def on_text_changed(self, flag):
        num = self.component.gridWidgets[f"{flag}Number"].text()
        self.update_table_rows(self.component.gridWidgets[f"{flag}Table"], num)

    def update_table_rows(self, table, num):
        try:
            num_rows = int(num)
            table.setRowCount(num_rows)
            table.set_columns_editable()
        except ValueError:
            table.setRowCount(0)

    def getTableDict(self, table, valueList, descriptionList, type):
        for i in range(table.rowCount()):
            rowDict = {}
            if type == "lapJoint":
                typeList = ["宽度", "下限", "初始值", "上限"]
            elif type == "strengthen":
                typeList = ["厚度", "下限", "初始值", "上限"]
            for j in range(table.columnCount()):
                item = table.item(i, j)
                rowDict[typeList[j]] = toFloat(item.text())
            valueList.append(rowDict)
            descriptionList.append(rowDict)
        return valueList

    @pyqtSlot()
    def on_pBtnOK_clicked(self):
        self.pBtnOK.setFocus()
        for flag in ["lapJoint", "strengthen"]:
            self.on_text_changed(flag=flag)

        valueRes = self.component.getValues()
        descriptionRes = self.component.getValuesUseDescription()

        valueRes["strengthenTable"] = []
        valueRes["lapJointTable"] = []

        descriptionRes["搭接"] = []
        descriptionRes["加强层"] = []

        self.getTableDict(
            self.component.gridWidgets["lapJointTable"],
            valueRes["lapJointTable"],
            descriptionRes["搭接"],
            "lapJoint",
        )
        self.getTableDict(
            self.component.gridWidgets["strengthenTable"],
            valueRes["strengthenTable"],
            descriptionRes["加强层"],
            "strengthen",
        )

        if not self.check_empty_values(descriptionRes["搭接"]):
            return envs.MBox.warning("请补搭接数据")
        if not self.check_empty_values(descriptionRes["加强层"]):
            return envs.MBox.warning("请补全加强层数据")

        file = "data/ipimas/uis/Optimization.json"
        Utils.write_json(file, valueRes)
        self.close()
        self.optimization_save_signal.emit(descriptionRes)

    def check_empty_values(self, data_list):
        for item in data_list:
            for key, value in item.items():
                if value == "" or value is None:
                    return False
        return True

    def is_table_filled(self, table):
        # 遍历表格中的每个单元格
        for row in range(self.table.rowCount()):
            for column in range(self.table.columnCount()):
                item = self.table.item(row, column)
                # 检查单元格是否为空
                if not item or item.text().strip() == "":
                    return False  # 如果找到空单元格，返回False
        return True  # 所有单元格都已填写

    def closeEvent(self, event):
        DlgOptimization._shown = False  # 使用类名访问类属性
        DlgOptimization._instance = None  # 清除实例引用
        event.accept()


def toFloat(value):
    try:
        return float(value)
    except ValueError:
        return None
